<script>
  import $ from '../dfish.js'
  import {
    h,
    Teleport,
    mergeProps
  } from 'vue'
  import View from './View.vue'
  import Pop from './Pop.vue'
  import ResizeObserver from 'resize-observer-polyfill'

  const O = {},
    T = true,
    F = false,
    N = null

  const _dialogSnapHooks = {
    t: 'tb',
    r: 'rl',
    b: 'bt',
    l: 'lr',
    c: 'cc',
    tl: '14',
    tr: '23',
    rt: '21',
    rb: '34',
    bl: '41',
    br: '32',
    lt: '12',
    lb: '43',
    '-t': 'tt',
    '-r': 'rr',
    '-b': 'bb',
    '-l': 'll',
    '-c': 'cc',
    '-tl': '11',
    '-tr': '22',
    '-rt': '22',
    '-rb': '33',
    '-bl': '44',
    '-br': '33',
    '-lt': '11',
    '-lb': '44',
  }

  const _dialogSnap = function(a) {
    let b = a && (a.position || a)
    return typeof b === 'string' && b.split(',').map(v => _dialogSnapHooks[v] || v).join()
  }

  export default {
    name: 'w-dialog',
    mixins: [View],
    props: ['title', 'snap', 'autoHide', 'context', 'prong', 'independent', 'timeout',
      'cover', 'position', 'local', 'fullScreen', 'dropClass', 'dropPosition', 'dropHover',
    ],
    emits: ['close', 'click:cover'],
    watch: {
      position(v) {
        this.axis()
      },
    },
    data() {
      return {
        isDialog: true,
        animateClass: '',
        snapClass: '',
        keepHover: false,
        hoverTimer: null,
        hoverMove: null,
        closed: false,
        animating: false,
        isDeactivated: false,
        _pos: null,
        snapStyle: null,
        maxHeight: null,
      }
    },
    activated() {
      this.isDeactivated = false
    },
    deactivated() {
      this.isDeactivated = true
    },
    methods: {
      getKey() {
        return null
      },
      getSrc() {
        let src = this.x.src
        if (!src) return ''
        const feat = this.getOptions()
        if (feat.args)
          src = $.urlFormat(src, feat.args)
        if (src.includes('$'))
          src = $.urlFormat(src, this.x.data, this.x)
        return src
      },
      _dft_pos() {
        let w = this.el().offsetWidth
        let h = this.el().offsetHeight
        return {
          left: Math.ceil((document.documentElement.clientWidth - w) / 2),
          top: Math.max(0, Math.ceil((document.documentElement.clientHeight - h) / 2))
        };
      },
      getLocalElem() {
        let c = this.getCommander()
        return c && c.el()
      },
      snapTargetElem() {
        let d = this.x.snap && this.x.snap.target
        if (typeof d === 'string') {
          let e = this.ownerView.find(d)
          return e && e.snapElem()
        } else if (typeof d === 'function') {
          return d()
        } else if (d) {
          return d.isWidget ? d.snapElem() : d
        } else {
          return this.x.local ? this.getLocalElem() : this.defaultSnapTargetElem
        }
      },
      snapCls(a) {
        let d = this.snapTargetElem()
        let r = this._pos
        if (d) {
          $.classAdd(d, 'z-drop', a)
          if (r && r.type) {
            var m = 'z-mag-' + (r.mag_t ? 't' : r.mag_l ? 'l' : r.mag_b ? 'b' : 'r')
            $.classAdd(this.el(), m + ' z-snap z-snap-' + r.type, a)
            $.classAdd(d, m, a)
          }
        }
      },
      axis() {
        let el = this.el()
        if (!el || this.x.fullScreen) return;
        let f = this.x.position
        if (typeof f === 'object') return;
        let c = this.x.local
        let d = this.x.snap || O
        let g = this.snapTargetElem()
        // 如果有指定 snap，采用 snap 模式
        let w = el.offsetWidth
        let h = el.offsetHeight
        let r;
        if (f == 'c') f = N;
        if (f) { // 八方位浮动的起始位置
          let e = f.charAt(0)
          if (e == '-') {
            e = f.charAt(1)
          }
          r = $.snap(w, h, c && g, _dialogSnap(f), this._fitpos, d.indent, c && g)
          if (e == 1 || e == 2 || e === 't') {
            r.top = -h;
          } else if (e == 3 || e == 4 || e === 'r') {
            r.right = -w;
          } else if (e == 5 || e == 6 || e === 'b') {
            r.bottom = -h;
          } else if (e == 7 || e == 8 || e === 'l') {
            r.left = -w;
          }
        } else if (g) {
          r = $.snap(w, h, g, _dialogSnap(d) || this.defaultSnapPosition || f, this._fitpos, d.indent != N ? d
            .indent : (this.x.prong && -10), c && g)
        }
        if (!r)
          r = this._dft_pos()
        this._pos && this.snapCls(F)
        this._pos = r
        
        let s = {}
        if (r.type) {
          if (r.pix_r) {
            s.right = r.right + 'px';
          } else {
            s.left = r.left + 'px';
          }
          if (r.pix_b) {
            s.bottom = r.bottom + 'px';
          } else {
            s.top = r.top + 'px';
          }
        } else {
          Object.keys(r).forEach(k => s[k] = $.unitSize(r[k]))
        }
        this.snapStyle = s
        // 八方位浮动效果
        if (f) {
          this.animateClass = 'w-dialog-ani-position-' + f
        }
        this.snapCls()
      },
      setPosition(s) {
        this.x.position = s
      },
      close() {
        if (this.closed) return
        this.closeRelease()
        this.trigger('close')
        this.ownerView.removeDialog(this.uid)
      },
      closeRelease() {
        this.x.autoHide && this.listenHide(F)
        this.snapCls(F)
        this.closed = true
      },
      // 绑定鼠标监听 /@a -> 为 false 时解除监听
      listenHide(a) {
        if (a === F) {
          this.setListenHide(a)
        } else {
          setTimeout(() => {
            if (!this.closed) {
              this.setListenHide(a)
            }
          }, 100) // 延时处理，避免出现后立即消失的情况
        }
      },
      hoverOver() {
        clearTimeout(this.hoverTimer)
        this.hoverTimer = null
      },
      setListenHide(a) {
        let d = this.x.dropHover
        let n = a ? 'addEventListener' : 'removeEventListener'
        let evs = $.br.mobile ? ['touchend'] : ['mousedown', $.br.firefox ? 'DOMMouseScroll' : 'mousewheel']
        evs.forEach(v => document[n](v, this.bind('listenHideClose', (e) => {
          if (!this.closed && (e.target.classList.contains('w-dialog-cover') || !(this.hasBubble(e.target) || (!
              this.x
              .independent && (this.commander.isWidget ? this.commander.hasBubble(e.target) : this.commander
                .contains(e.target)))))) {
            if (e.target.id == this.id + 'cvr') e.stopPropagation()
            this.close()
          }
        }), T))
        window.plus && plus.key[n]('backbutton', this.bind('close'), F)
        if (d) {
          let o = d === T ? (this.x.snap && this.x.snap.target ? this.snapTargetElem() : $.el(this.commander)) : $.el(d)
          let hvOver = this.bind('listenHideHoverOver', e => {
            clearTimeout(this.hoverTimer), this.hoverTimer = null
          });
          let hvMove = this.bind('listenHideHoverMove', e => {
            if (!o.contains(e.target) && !this.hasBubble(e.target) && !this.keepHover) {
              if (!this.hoverTimer)
                this.hoverTimer = setTimeout(this.close, 300)
            } else
              hvOver()
          });
          [o, this.el()].forEach(v => v[n]('mouseenter', hvOver))
          document[n]('mousemove', hvMove)
        }
      },
      setMaxHeight(h) {
        this.maxHeight = h
      },
      getOptions() {
        return this.ownerView.getDialogOptions(this.uid)
      },
      getCommander() {
        let o = this.getOptions()
        return (o && o.commander) || this.context || this.ownerView
      },
      html_slots() {
        let prong
        let r = this._pos
        if (this.x.prong && r) {
          let m = r.inner ? (r.mag_b ? 'b' : r.mag_t ? 't' : r.mag_l ? 'l' : 'r') : (r.mag_b ? 't' : r.mag_t ? 'b' : r
            .mag_l ? 'r' : 'l')
          let x = Math.floor((r.target.left + r.target.right) / 2)
          let y = Math.floor((r.target.top + r.target.bottom) / 2)
          let l = $.numRange(x - r.left, 7, r.left + r.width - 7)
          let t = $.numRange(y - r.top, 7, r.top + r.height - 7)
          let s = ''
          if (r.mag_b || r.mag_t) s += 'left:' + l + 'px;'
          if (r.mag_l || r.mag_r) s += 'top:' + t + 'px;'
          prong = h('div', {
            class: 'w-dialog-prong z-' + m,
            style: s
          }, [
            h('i', {
              class: 'w-dialog-prong-out f-arw f-arw-' + m + '5'
            }),
            h('i', {
              class: 'w-dialog-prong-in f-arw f-arw-' + m + '4'
            }),
          ])
        }
        return [prong || [], this.html_scroll_slots()]
      },
      html() {
        let to = this.x.local ? this.getLocalElem() : '#w-dfish-pops'
        let cover = this.x.cover && !this.closed && !this.isDeactivated ? h('div', {
          class: 'w-dialog-cover f-abs-full',
          onclick: e => this.trigger('click:cover', e)
        }) : null
        return [
          cover ? (to ? h(Teleport, {to}, cover) : cover) : [],
          to ? h(Teleport, {to}, this.html_self(true)) : this.html_self(true),
          this.html_pops()
        ]
      }
    },
    computed: {
      commander() {
        return this.getCommander()
      },
      args() {
        let o = this.getOptions()
        return o && o.args
      },
      defaultSnapTargetElem() {
        //implement
      },
      defaultSnapPosition() {
        return 'cc'
      },
      html_class() {
        return ['w-vertical', this.animateClass, this.snapClass, {
          'z-hide': this.isDeactivated,
          'z-full': this.x.fullScreen,
        }]
      },
      html_style() {
        return this.x.fullScreen ? {
            width: '100%',
            height: '100%',
            top: 0,
            left: 0,
          } : {
            ...this.snapStyle,
            maxHeight: $.unitSize(this.maxHeight),
            ...(this.x.position && typeof this.x.position === 'object' ? this.x.position : null),
          }
      }
    },
    mounted() {
      if (this.isEntity) {
        if (this.x.local) {
          $.q(this.getLocalElem()).addClass('f-dialog-local-wrap')
        }
        this.$nextTick(() => {
          this.x.autoHide && this.listenHide(T)
          setTimeout(this.axis)
          const observe = (o) => {
            if (this.closed || this.animateClass || !this.el()) return
            this.axis()
            if (this.x.position) {
              this.setMaxHeight('100%')
            } else {
              let c = this._pos
              if (c.bottom < 0) {
                this.setMaxHeight(c.height + c.bottom)
              } else if (c.top < 0) {
                this.setMaxHeight(c.height + c.top)
              }
            }
          }
          new ResizeObserver(observe).observe(this.el())
          if (this.x.timeout) {
            setTimeout(this.close, this.x.timeout)
          }
        })
      }
    },
    unmounted() {
      if (this.isEntity && !this.closed) {
        this.closeRelease()
      }
    }
  }
</script>

<style>
  .w-dialog {
    display: inline-flex;
    position: absolute;
    box-shadow: 0px 0px 8px #bbb;
    z-index: 100;
    box-sizing: border-box;
    --w-dialog-ani-duration: 200ms;
  }

  .w-dialog.z-hide {
    display: none;
  }

  .w-dialog.z-full {
    transform: none;
    transition: none;
  }

  .w-dialog-cover {
    background: rgba(0, 0, 0, .3);
    z-index: 99;
  }

  .w-dialog-prong {
    position: absolute;
  }

  .w-dialog-prong .f-arw {
    position: absolute;
    margin: 0
  }

  .w-dialog-prong.z-b {
    width: 9px;
    height: 8px;
    left: 50%;
    margin-left: -9px;
    bottom: -8px;
  }

  .w-dialog-prong.z-b .w-dialog-prong-in {
    top: 0;
    left: 1px;
    border-top-color: #fff
  }

  .w-dialog-prong.z-b .w-dialog-prong-out {
    top: 0;
    left: 0;
    border-top-color: #d5d5d5
  }

  .w-dialog-prong.z-t {
    width: 9px;
    height: 8px;
    left: 50%;
    margin-left: -9px;
    top: -8px;
  }

  .w-dialog-prong.z-t .w-dialog-prong-in {
    bottom: 0;
    left: 1px;
    border-bottom-color: #fff
  }

  .w-dialog-prong.z-t .w-dialog-prong-out {
    bottom: 0;
    left: 0;
    border-bottom-color: #d5d5d5
  }

  .w-dialog-prong.z-l {
    width: 8px;
    height: 9px;
    top: 50%;
    margin-top: -9px;
    left: -8px;
  }

  .w-dialog-prong.z-l .w-dialog-prong-in {
    right: 0;
    top: 1px;
    border-right-color: #fff
  }

  .w-dialog-prong.z-l .w-dialog-prong-out {
    right: 0;
    top: 0;
    border-right-color: #d5d5d5
  }

  .w-dialog-prong.z-r {
    width: 8px;
    height: 9px;
    top: 50%;
    margin-top: -9px;
    right: -8px;
  }

  .w-dialog-prong.z-r .w-dialog-prong-in {
    left: 0;
    top: 1px;
    border-left-color: #fff
  }

  .w-dialog-prong.z-r .w-dialog-prong-out {
    left: 0;
    top: 0;
    border-left-color: #d5d5d5
  }

  .w-dialog-ani-position-br,
  .w-dialog-ani-position-bl,
  .w-dialog-ani-position-b {
    transform: translateY(-100%);
    transition: transform var(--w-dialog-ani-duration);
  }

  .w-dialog-ani-position--br,
  .w-dialog-ani-position--bl,
  .w-dialog-ani-position--b {
    transform: translateY(0);
    transition: transform var(--w-dialog-ani-duration);
  }

  .w-dialog-ani-position-tr,
  .w-dialog-ani-position-tl,
  .w-dialog-ani-position-t {
    transform: translateY(100%);
    transition: transform var(--w-dialog-ani-duration);
  }

  .w-dialog-ani-position-lt,
  .w-dialog-ani-position-lb,
  .w-dialog-ani-position-l {
    transform: translateX(100%);
    transition: transform var(--w-dialog-ani-duration);
  }

  .w-dialog-ani-position-rt,
  .w-dialog-ani-position-rb,
  .w-dialog-ani-position-r {
    transform: translateX(-100%);
    transition: transform var(--w-dialog-ani-duration);
  }

  .w-dialog-ani-position--rt,
  .w-dialog-ani-position--rb,
  .w-dialog-ani-position--r {
    transform: translateX(0);
    transition: transform var(--w-dialog-ani-duration);
  }
</style>
